#include "EventNetwork.h"

#include "EventEntry.h"
#include "GroupEntry.h"
#include "MemberManager.h"

#include <arpa/inet.h>
#include <libintl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#define _(String) gettext(String)

EventNetwork *event_network = NULL;
//
static struct timeval timeout;

struct _EventNetwork
{
	EventFileDriver parent_instance;
	//
	struct sockaddr server_address;
	EventString    *address_str;
	/* 用户手机号码 */
	euint64	     phone;
	EventString *phone_str;
	/* 用户密码 */
	EventString *password;
	/* 用户名 */
	EventString *user_name;
	/* 当前连接出错的原因 */
	EventString *error_info;
	/* OnlineID */
	euint64 online_id;
	/* 可选的验证码 */
	eint32 verification_code;
	/* 绑定的本地套接字 */
	int local_socket;
	/* 最后一次网络操作的返回值 */
	ResponseType re_type;
	/* 当前是否在线 */
	bool is_online;
	/* 本对象是否已初始化 */
	bool is_init;
	/* 本对象是否包含了默认的配置 */
	bool is_have_local_property;
};

static void
event_network_finalize(EventNetwork *self)
{
	event_file_driver_dump(EVENT_FILE_DRIVER(self));
	event_file_driver_finalize(EVENT_FILE_DRIVER(self));
	event_object_unref(self->address_str);
	event_object_unref(self->password);
	event_object_unref(self->user_name);
	event_object_unref(self->phone_str);
	if (self->is_init)
		close(self->local_socket);
}

static void
update_socket(EventNetwork *self)
{
	self->is_init = false;
	if (self->local_socket != -1) {
		if (close(self->local_socket))
			perror("close");
	}
	self->local_socket = socket(AF_INET, SOCK_STREAM, 0);
	if (self->local_socket == -1) {
		perror("socket");
		return;
	}

	self->is_init = true;
	return;
}

static int
event_network_property_parser(EventFileDriver *driver, const epointer data,
			      int length)
{
	static char   buffer[12];
	EventNetwork *self = EVENT_NETWORK(driver);
	EventList    *list = event_list_new_with_raw(data), *args;
	EventString  *address;
	int	      status;

	if (!list)
		return 1;
	self->is_have_local_property = true;
	//
	args = list;
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT64))
		return 0;
	self->online_id = EN_GET_D(args, euint64);
	args		= EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT64))
		return 0;
	self->phone = EN_GET_D(args, euint64);
	sprintf(buffer, "%011lu", self->phone);
	self->phone_str = event_string_new(buffer);
	args		= EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_STRING))
		return 0;
	self->password = EO_REF(EN_GET_D(args, EventString *), EventString);
	args	       = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_STRING))
		return 0;
	address = EN_GET_D(args, EventString *);
	if (!address)
		return 0;
	status = event_network_set_server_address(self, ES_GET(address));
	if (status) {
		event_debug(EventNetwork, event_network_property_parser,
			    "设置 IP 地址时出现错误。");
	}
	event_object_unref(list);
	return 0;
}

static EventFileDriverPack *
event_network_property_writer(EventFileDriver *driver)
{
	EventList    *list;
	EventRaw     *raw_object;
	EventNetwork *self = EVENT_NETWORK(driver);
	//
	EventFileDriverPack *pack;

	list = event_list_new();
	event_list_add_data(list, EPTR(self->online_id), EVENT_TYPE_UINT64,
			    NULL);
	event_list_add_data(list, EPTR(self->phone), EVENT_TYPE_UINT64, NULL);
	event_list_add_data(list, self->password, EVENT_TYPE_STRING, NULL);
	event_list_add_data(list, self->address_str, EVENT_TYPE_STRING, NULL);
	raw_object = event_list_export_raw(list);
	event_object_unref(list);
	if (!raw_object) {
		return_val_if_fail(raw_object != NULL,
				   event_network_property_writer, NULL);
	}
	pack = event_file_driver_pack_new_from_raw(raw_object);
	return pack;
}

EventNetwork *
event_network_new(void)
{
	EventNetwork *object = MALLOC_NEW(EventNetwork);

	event_file_driver_init(EVENT_FILE_DRIVER(object));
	event_object_set_finalize(object, FINALIZE(event_network_finalize));
	object->is_init	     = false;
	object->address_str  = NULL;
	object->phone	     = 0L;
	object->password     = NULL;
	object->online_id    = 0L;
	object->local_socket = -1;
	object->error_info   = NULL;
	object->user_name    = NULL;
	object->phone_str    = NULL;
	object->re_type	     = RESPONSE_TYPE_NONE;
	//
	object->is_have_local_property = false;
	object->verification_code      = 0;
	//
	timeout.tv_sec	= 1;
	timeout.tv_usec = 0;
	//
	event_file_driver_set_writer(EVENT_FILE_DRIVER(object),
				     event_network_property_writer, NULL);
	event_file_driver_set_parser(EVENT_FILE_DRIVER(object),
				     event_network_property_parser);

	return object;
}

int
event_network_check_address(const char *address, struct sockaddr *sock_addr)
{
	struct sockaddr_in *addr, _addr;
	return_val_if_fail(address != NULL, event_network_check_address, -1);
	if (sock_addr)
		addr = (struct sockaddr_in *)sock_addr;
	else
		addr = &_addr;
	addr->sin_family = AF_INET;
	addr->sin_port	 = htons(N_OBJ_PORT);
	return inet_pton(AF_INET, address, &addr->sin_addr);
}

void
event_network_clean_online_id(EventNetwork *self)
{
	return_if_fail(self != NULL, event_network_clean_online_id);
	self->online_id = 0;
	event_file_driver_dump(EVENT_FILE_DRIVER(self));
}

int
event_network_set_server_address(EventNetwork *self, const char *address)
{
	int status;
	return_val_if_fail(self != NULL, event_network_set_server_address, 1);

	status = event_network_check_address(address, &self->server_address);
	if (status == 1) {
		if (self->address_str)
			event_object_unref(self->address_str);
		self->address_str = event_string_new(address);
		return 0;
	}
	return 1;
}

void
event_network_set_phone_password(EventNetwork *self, const char *phone_str,
				 const char *password)
{
	char *__end_tmp;
	return_if_fail(self != NULL, event_network_set_phone_password);
	if (phone_str) {
		self->phone = strtoul(phone_str, &__end_tmp, 10);
		if (self->phone_str)
			event_object_unref(self->phone_str);
		self->phone_str = event_string_new(phone_str);
	}

	if (password) {
		if (self->password)
			event_object_unref(self->password);
		self->password = event_string_new(password);
	}
}

void
event_network_set_user_name(EventNetwork *self, const char *user_name)
{
	return_if_fail(self != NULL, event_network_set_user_name);
	if (self->user_name)
		event_object_unref(self->user_name);
	self->user_name = event_string_new(user_name);
}

void
event_network_set_verification_code(EventNetwork *self, const char *vcode)
{
	char *__end_tmp;
	return_if_fail(self != NULL, event_network_set_verification_code);
	if (vcode)
		self->verification_code = (eint32)strtol(vcode, &__end_tmp, 10);
	else
		self->verification_code = 0;
}

static void
set_error_info(EventNetwork *self, const char *error_info)
{
	if (self->error_info)
		event_object_unref(self->error_info);
	if (error_info)
		self->error_info = event_string_new(error_info);
	else
		self->error_info = NULL;
}

/* 出错返回 -1，无需处理出错信息
   将接管 EventRaw 对象！ */
static int
send_with_raw_object(EventNetwork *self, EventRaw *raw_object)
{
	epointer raw_data;
	euint32	 raw_size;
	int	 status;
	return_val_if_fail(raw_object != NULL, send_with_raw_object, -1);
	raw_data = event_raw_get_data(raw_object);
	raw_size = event_raw_get_size(raw_object);
	setsockopt(self->local_socket, SOL_SOCKET, SO_SNDTIMEO, &timeout,
		   sizeof(timeout));
	status = send(self->local_socket, raw_data, raw_size, 0);
	if (status == -1) {
		set_error_info(self, _("Error sending data packet."));
		perror("send");
	}
	event_object_unref(raw_object);
	return status;
}

/* 连接到客户端，出错返回 -1。无需处理出错信息 */
static int
connect_server(EventNetwork *self)
{
	int status;

	if (self->local_socket != -1)
		return 0;
	update_socket(self);
	setsockopt(self->local_socket, SOL_SOCKET, SO_SNDTIMEO, &timeout,
		   sizeof(timeout));
	status = connect(self->local_socket, &self->server_address,
			 sizeof(struct sockaddr));
	if (status == -1) {
		set_error_info(self, _("Error connecting to server."));
		perror("connect");
	} else {
		self->is_init = true;
	}
	return status;
}

/**
 * 创建一个可直接发送的 EventList。
 * 注意！将会接管 EventList *args。
 */
static EventList *
generate_send_list(euint64 online_id, ExecuteType exe_type, EventList *args)
{
	EventList *send_list = event_list_new();
	event_list_add_data(send_list, EPTR(online_id), EVENT_TYPE_UINT64,
			    NULL);
	event_list_add_data(send_list, EPTR((euint64)exe_type),
			    EVENT_TYPE_INT32, NULL);
	if (args) {
		event_list_add_data(send_list, args, EVENT_TYPE_LIST,
				    event_object_unref);
	}
	return send_list;
}

static EventList *
receive_from_server(EventNetwork *self)
{
	static char buffer[4096];
	EventList  *list;
	int	    status;

	setsockopt(self->local_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout,
		   sizeof(timeout));
	status = recv(self->local_socket, buffer, 4096, 0);
	event_debug(EventNetwork, receive_from_server, "recved %d bytes",
		    status);
	if (status <= 0) {
		set_error_info(self, _("The server closed the connection."));
		perror("recv");
		return NULL;
	}

	list = event_list_new_with_raw(buffer);
	if (!list) {
		set_error_info(self, _("No valid server return was received."));
		return NULL;
	}
	return list;
}

/* 出错返回 1，无错返回 0，无需处理出错信息 */
static int
network_prepare(EventNetwork *self)
{
	if (self == NULL || self->password == NULL || self->phone == 0)
		goto initialization_error;
	if (self->is_init)
		return 0;
	if (connect_server(self) == -1)
		return 1;
	return 0;
initialization_error:
	set_error_info(self, _("Server initialization error."));
	return_val_if_fail(self != NULL, network_prepare, 1);
	return_val_if_fail(self->password != NULL, network_prepare, 1);
	return_val_if_fail(self->phone != 0L, network_prepare, 1);
	return_val_if_fail(self->is_init == true, network_prepare, 1);
	return 1;
}

/* 将会接管 EventList，并且处理错误信息。 */
static EventRaw *
generate_raw_object(EventNetwork *self, EventList *list)
{
	EventRaw *raw_object;
	raw_object = event_list_export_raw(list);
	event_object_unref(list);
	if (!raw_object) {
		set_error_info(self,
			       _("An error occurred while exporting EventList. "
				 "(System bug, report it)"));
	}
	return raw_object;
}

/* 与服务器交换信息，并且验证从服务器返回的信息。
   若有误，将返回 NULL，无需处理出错信息。
   返回的 EventList 可以直接获取 ResponseType */
static EventList *
exchange_data_with_server(EventNetwork *self, EventList *send_list)
{
	EventRaw  *raw_object;
	EventList *list;
	raw_object = generate_raw_object(self, send_list);
	if (!raw_object)
		goto error_and_exit;

	if (send_with_raw_object(self, raw_object) == -1)
		goto error_and_exit;

	list = receive_from_server(self);
	if (!list)
		goto error_and_exit;

	if (!event_list_verify_node_type(list, EVENT_TYPE_INT32)) {
		event_object_unref(list);
		set_error_info(self,
			       _("The server returned an invalid header."));
		return NULL;
	}
	return list;
error_and_exit:
	return NULL;
}

ResponseType
event_network_try_logon(EventNetwork *self)
{
	EventList *list = NULL, *args;
	self->re_type	= RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	/* 生成客户端请求 */
	args = event_list_new();
	event_list_add_data(args, EPTR(self->phone), EVENT_TYPE_UINT64, NULL);
	event_list_add_data(args, self->password, EVENT_TYPE_STRING, NULL);

	/* 是否使用验证码登陆 */
	if (self->verification_code != 0) {
		event_list_add_data(args,
				    EPTR((euint64)self->verification_code),
				    EVENT_TYPE_INT32, NULL);
		list = generate_send_list(self->online_id, EXECUTE_TYPE_LOGONV,
					  args);
	} else {
		list = generate_send_list(self->online_id, EXECUTE_TYPE_LOGON,
					  args);
	}

	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (eint32)EN_GET_D(args, euint64);
	args	      = EN_GET_NEXT(args, EventList *);
	if (args && event_list_verify_node_type(args, EVENT_TYPE_UINT64)) {
		self->online_id = EN_GET_D(args, euint64);
		args		= EN_GET_NEXT(args, EventList *);
	}
	if (args && event_list_verify_node_type(args, EVENT_TYPE_STRING)) {
		if (self->user_name)
			event_object_unref(self->user_name);
		self->user_name =
			EO_REF(EN_GET_D(args, EventString *), EventString);
	}

	set_error_info(self, NULL);

response_type_none_exit:
	event_object_unref(list);
	if (!self->is_have_local_property &&
	    self->re_type == RESPONSE_TYPE_RETURN_VERIFY_CODE) {
		event_file_driver_add_count_once(EVENT_FILE_DRIVER(self));
	}
	return self->re_type;
}

ResponseType
event_network_try_register(EventNetwork *self)
{
	EventList *args, *list = NULL;
	self->re_type = RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	args = event_list_new();
	event_list_add_data(args, EPTR(self->phone), EVENT_TYPE_UINT64, NULL);
	event_list_add_data(args, self->user_name, EVENT_TYPE_STRING, NULL);
	event_list_add_data(args, self->password, EVENT_TYPE_STRING, NULL);
	list = generate_send_list(self->online_id, EXECUTE_TYPE_REGISTER, args);

	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (eint32)EN_GET_D(list, euint64);

response_type_none_exit:
	event_object_unref(list);
	return self->re_type;
}

const char *
event_network_get_error_info(EventNetwork *self)
{
	return_val_if_fail(self != NULL, event_network_get_error_info, NULL);
	if (!self->error_info)
		return NULL;
	return event_string_get_string(self->error_info);
}

bool
event_network_have_local_property(EventNetwork *self)
{
	return_val_if_fail(self != NULL, event_network_have_local_property,
			   false);
	return self->is_have_local_property;
}

const char *
event_network_get_phone(EventNetwork *self)
{
	return_val_if_fail(self != NULL, event_network_get_phone, 0);
	return ES_GET(self->phone_str);
}

const char *
event_network_get_password(EventNetwork *self)
{
	return_val_if_fail(self != NULL, event_network_get_password, NULL);
	return ES_GET(self->password);
}

const char *
event_network_get_address(EventNetwork *self)
{
	return_val_if_fail(self != NULL, event_network_get_address, NULL);
	return ES_GET(self->address_str);
}

ResponseType
event_network_get_response_type(EventNetwork *self)
{
	return_val_if_fail(self != NULL, event_network_get_response_type,
			   RESPONSE_TYPE_NONE);
	return self->re_type;
}

/* 从 EventList 中解析出 ObjectArray 对象 */
static ObjectArray *
parser_all_groups(EventList *list)
{
	ObjectArray *obj_array = NULL;
	EventList   *args;
	EventString *group_name;
	GroupEntry  *group_entry;
	euint32	     group_id;
	bool	     is_creator, can_write, can_join, g_can_write;

	if (!list)
		return NULL;
retry:
	args = EN_GET_D(list, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT32))
		goto get_next;
	group_id = (euint32)EN_GET_D(args, euint64);

	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_STRING))
		goto get_next;
	group_name = EN_GET_D(args, EventString *);

	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT8))
		goto get_next;
	is_creator = (bool)EN_GET_D(args, euint64);

	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT8))
		goto get_next;
	can_write = (bool)EN_GET_D(args, euint64);

	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT8))
		goto get_next;
	can_join = (bool)EN_GET_D(args, euint64);

	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT8))
		goto get_next;
	g_can_write = (bool)EN_GET_D(args, euint64);

	if (!obj_array) {
		obj_array = object_array_new(50);
		object_array_set_object_finalize(obj_array, g_object_unref);
	}
	group_entry = GROUP_ENTRY(group_entry_new());
	group_entry_set_group_id(group_entry, group_id);
	group_entry_set_group_name(group_entry, ES_GET(group_name));
	group_entry_set_is_creator(group_entry, is_creator);
	group_entry_set_can_write(group_entry, can_write);
	group_entry_set_can_join(group_entry, can_join);
	group_entry_set_global_can_write(group_entry, g_can_write);
	object_array_insert_object(obj_array, group_entry);

get_next:
	list = EN_GET_NEXT(list, EventList *);
	if (list)
		goto retry;
	return obj_array;
}

/* 从 EventList 中解析出 ObjectArray 对象 */
static ObjectArray *
parser_all_events(EventList *list)
{
	ObjectArray *obj_array = NULL;
	EventList   *args;
	EventString *group_name, *event_name, *event_describe;
	EventEntry  *event_entry;
	time_t	     end_time;
	euint32	     group_id, event_id;
	bool	     can_write, is_important;

	if (!list)
		return NULL;
retry:
	args = EN_GET_D(list, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT32))
		goto get_next;
	group_id = (euint32)EN_GET_D(args, euint64);

	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_STRING))
		goto get_next;
	group_name = EN_GET_D(args, EventString *);

	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT32))
		goto get_next;
	event_id = (euint32)EN_GET_D(args, euint64);

	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_INT64))
		goto get_next;
	end_time = EN_GET_D(args, time_t);

	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_STRING))
		goto get_next;
	event_name = EN_GET_D(args, EventString *);

	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_STRING))
		goto get_next;
	event_describe = EN_GET_D(args, EventString *);

	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT8))
		goto get_next;
	can_write = (bool)EN_GET_D(args, euint64);

	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT8))
		goto get_next;
	is_important = (bool)EN_GET_D(args, euint64);

	if (!obj_array) {
		obj_array = object_array_new(50);
		object_array_set_object_finalize(obj_array, g_object_unref);
	}

	event_entry = EVENT_ENTRY(event_entry_new());
	event_entry_set_group_id(event_entry, group_id);
	event_entry_set_group_name(event_entry, ES_GET(group_name));
	event_entry_set_event_id(event_entry, event_id);
	event_entry_set_end_time(event_entry, end_time);
	event_entry_set_event_name(event_entry, ES_GET(event_name));
	event_entry_set_event_describe(event_entry, ES_GET(event_describe));
	event_entry_set_can_modify(event_entry, can_write);
	event_entry_set_is_important(event_entry, is_important);
	object_array_insert_object(obj_array, event_entry);

get_next:
	list = EN_GET_NEXT(list, EventList *);
	if (list)
		goto retry;
	return obj_array;
}

ObjectArray *
event_network_fetch_all_groups(EventNetwork *self)
{
	EventList   *list      = NULL, *args;
	ObjectArray *obj_array = NULL;

	return_val_if_fail(self != NULL, event_network_fetch_all_groups, NULL);
	self->re_type = RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	list = generate_send_list(self->online_id, EXECUTE_TYPE_FETCH_ALL_GROUP,
				  NULL);

	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (eint32)EN_GET_D(args, eint64);
	if (self->re_type != RESPONSE_TYPE_SUCCESS)
		goto response_type_none_exit;
	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_LIST))
		goto response_type_none_exit;
	args = EN_GET_D(args, EventList *);

	obj_array = parser_all_groups(args);

	set_error_info(self, NULL);
response_type_none_exit:
	event_object_unref(list);
	return obj_array;
}

ObjectArray *
event_network_fetch_all_events(EventNetwork *self)
{
	EventList   *list      = NULL, *args;
	ObjectArray *obj_array = NULL;

	return_val_if_fail(self != NULL, event_network_fetch_all_events, NULL);
	self->re_type = RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	list = generate_send_list(self->online_id, EXECUTE_TYPE_FETCH_ALL_EVENT,
				  NULL);
	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (eint32)EN_GET_D(args, eint64);
	if (self->re_type != RESPONSE_TYPE_SUCCESS)
		goto response_type_none_exit;
	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_LIST))
		goto response_type_none_exit;
	args = EN_GET_D(args, EventList *);

	obj_array = parser_all_events(args);

	set_error_info(self, NULL);
response_type_none_exit:
	event_object_unref(list);
	return obj_array;
}

euint32
event_network_add_new_event(EventNetwork *self, euint32 group_id,
			    time_t end_time, const char *event_name,
			    const char *event_describe, bool is_important)
{
	EventList *list		= NULL, *args;
	euint32	   new_event_id = 0;

	return_val_if_fail(self != NULL, event_network_add_new_event, 0);
	self->re_type = RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	args = event_list_new();
	event_list_add_data(args, EPTR((euint64)group_id), EVENT_TYPE_UINT32,
			    NULL);
	event_list_add_data(args, EPTR(end_time), EVENT_TYPE_INT64, NULL);
	event_list_add_data(args, event_string_new(event_name),
			    EVENT_TYPE_STRING, event_object_unref);
	event_list_add_data(args, event_string_new(event_describe),
			    EVENT_TYPE_STRING, event_object_unref);
	event_list_add_data(args, EPTR((euint64)is_important), EVENT_TYPE_UINT8,
			    NULL);
	list = generate_send_list(self->online_id, EXECUTE_TYPE_CREATE_EVENT,
				  args);
	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (eint32)EN_GET_D(args, eint64);
	if (self->re_type != RESPONSE_TYPE_SUCCESS)
		goto response_type_none_exit;
	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT32))
		goto response_type_none_exit;
	new_event_id = (euint32)EN_GET_D(args, euint64);

	set_error_info(self, NULL);
response_type_none_exit:
	event_object_unref(list);
	return new_event_id;
}

euint32
event_network_add_new_group(EventNetwork *self, const char *group_name,
			    bool writable, bool joinable)
{
	EventList *list, *args;
	euint32	   new_group_id = 0;

	return_val_if_fail(self != NULL, event_network_add_new_group, 0);
	self->re_type = RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	args = event_list_new();
	event_list_add_data(args, event_string_new(group_name),
			    EVENT_TYPE_STRING, event_object_unref);
	event_list_add_data(args, EPTR((euint64)writable), EVENT_TYPE_UINT8,
			    NULL);
	event_list_add_data(args, EPTR((euint64)joinable), EVENT_TYPE_UINT8,
			    NULL);
	list = generate_send_list(self->online_id, EXECUTE_TYPE_CREATE_GROUP,
				  args);

	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (euint32)EN_GET_D(args, euint64);
	if (self->re_type != RESPONSE_TYPE_SUCCESS)
		goto response_type_none_exit;
	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT32))
		goto response_type_none_exit;
	new_group_id = (euint32)EN_GET_D(args, euint64);

	set_error_info(self, NULL);
response_type_none_exit:
	event_object_unref(list);
	return new_group_id;
}

ResponseType
event_network_modify_event(EventNetwork *self, euint32 group_id,
			   euint32 event_id, eint64 end_time,
			   const char *event_name, const char *event_describe,
			   bool is_important)
{
	EventList *list, *args;

	return_val_if_fail(self != NULL, event_network_modify_event, 0);
	self->re_type = RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	args = event_list_new();
	event_list_add_data(args, EPTR((euint64)group_id), EVENT_TYPE_UINT32,
			    NULL);
	event_list_add_data(args, EPTR((euint64)event_id), EVENT_TYPE_UINT32,
			    NULL);
	event_list_add_data(args, EPTR(end_time), EVENT_TYPE_INT64, NULL);
	event_list_add_data(args, event_string_new(event_name),
			    EVENT_TYPE_STRING, event_object_unref);
	event_list_add_data(args, event_string_new(event_describe),
			    EVENT_TYPE_STRING, event_object_unref);
	event_list_add_data(args, EPTR((euint64)is_important), EVENT_TYPE_UINT8,
			    NULL);
	list = generate_send_list(self->online_id,
				  EXECUTE_TYPE_MODIFY_EVENT_ITEM, args);

	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (euint32)EN_GET_D(args, euint64);

	set_error_info(self, NULL);
response_type_none_exit:
	event_object_unref(list);
	return self->re_type;
}

ResponseType
event_network_remove_event(EventNetwork *self, euint32 group_id,
			   euint32 event_id)
{
	EventList *list, *args;

	return_val_if_fail(self != NULL, event_network_remove_event, 0);
	self->re_type = RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	args = event_list_new();
	event_list_add_data(args, EPTR((euint64)group_id), EVENT_TYPE_UINT32,
			    NULL);
	event_list_add_data(args, EPTR((euint64)event_id), EVENT_TYPE_UINT32,
			    NULL);
	list = generate_send_list(self->online_id,
				  EXECUTE_TYPE_REMOVE_EVENT_FROM_GROUP, args);

	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (euint32)EN_GET_D(args, euint64);

	set_error_info(self, NULL);
response_type_none_exit:
	event_object_unref(list);
	return self->re_type;
}

EventString *
event_network_join_group(EventNetwork *self, euint32 group_id)
{
	EventList   *list, *args;
	EventString *group_name = NULL;

	return_val_if_fail(self != NULL, event_network_join_group, 0);
	self->re_type = RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	args = event_list_new();
	event_list_add_data(args, EPTR((euint64)group_id), EVENT_TYPE_UINT32,
			    NULL);
	list = generate_send_list(self->online_id, EXECUTE_TYPE_JOIN_GROUP,
				  args);

	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (euint32)EN_GET_D(args, euint64);
	args	      = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_STRING))
		goto response_type_none_exit;
	group_name = EO_REF(EN_GET_D(args, EventString *), EventString);

	set_error_info(self, NULL);
response_type_none_exit:
	event_object_unref(list);
	return group_name;
}

ResponseType
event_network_modify_group_name(EventNetwork *self, euint32 group_id,
				const char *group_name)
{
	EventList *list, *args;

	return_val_if_fail(self != NULL, event_network_modify_group_name,
			   RESPONSE_TYPE_NONE);
	self->re_type = RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	args = event_list_new();
	event_list_add_data(args, EPTR((euint64)group_id), EVENT_TYPE_UINT32,
			    NULL);
	event_list_add_data(args, event_string_new(group_name),
			    EVENT_TYPE_STRING, event_object_unref);
	list = generate_send_list(self->online_id,
				  EXECUTE_TYPE_MODIFY_GROUP_NAME, args);

	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (euint32)EN_GET_D(args, euint64);

	set_error_info(self, NULL);
response_type_none_exit:
	event_object_unref(list);
	return self->re_type;
}

ResponseType
event_network_modify_g_can_write(EventNetwork *self, euint32 group_id,
				 bool g_can_write)
{
	EventList *list, *args;

	return_val_if_fail(self != NULL, event_network_modify_g_can_write,
			   RESPONSE_TYPE_NONE);
	self->re_type = RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	args = event_list_new();
	event_list_add_data(args, EPTR((euint64)group_id), EVENT_TYPE_UINT32,
			    NULL);
	event_list_add_data(args, EPTR((euint64)g_can_write), EVENT_TYPE_UINT8,
			    NULL);
	list = generate_send_list(self->online_id,
				  EXECUTE_TYPE_MODIFY_GROUP_CAN_WRITE_FLAG,
				  args);

	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (euint32)EN_GET_D(args, euint64);

	set_error_info(self, NULL);
response_type_none_exit:
	event_object_unref(list);
	return self->re_type;
}

ResponseType
event_network_modify_can_join(EventNetwork *self, euint32 group_id,
			      bool can_join)
{
	EventList *list, *args;

	return_val_if_fail(self != NULL, event_network_modify_can_join,
			   RESPONSE_TYPE_NONE);
	self->re_type = RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	args = event_list_new();
	event_list_add_data(args, EPTR((euint64)group_id), EVENT_TYPE_UINT32,
			    NULL);
	event_list_add_data(args, EPTR((euint64)can_join), EVENT_TYPE_UINT8,
			    NULL);
	list = generate_send_list(
		self->online_id, EXECUTE_TYPE_MODIFY_GROUP_CAN_JOIN_FLAG, args);

	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (euint32)EN_GET_D(args, euint64);

	set_error_info(self, NULL);
response_type_none_exit:
	event_object_unref(list);
	return self->re_type;
}

ResponseType
event_network_remove_group(EventNetwork *self, euint32 group_id)
{
	EventList *list, *args;

	return_val_if_fail(self != NULL, event_network_remove_group,
			   RESPONSE_TYPE_NONE);
	self->re_type = RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	args = event_list_new();
	event_list_add_data(args, EPTR((euint64)group_id), EVENT_TYPE_UINT32,
			    NULL);
	list = generate_send_list(self->online_id, EXECUTE_TYPE_REMOVE_GROUP,
				  args);

	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (euint32)EN_GET_D(args, euint64);

	set_error_info(self, NULL);
response_type_none_exit:
	event_object_unref(list);
	return self->re_type;
}

static ObjectArray *
parser_all_member_info(EventList *list)
{
	ObjectArray *obj_array = NULL;
	EventList   *args;
	EventString *user_name;
	MemberEntry *member_entry;
	euint32	     user_id, group_id;
	bool	     can_write;

	if (!list)
		return NULL;
retry:
	args = EN_GET_D(list, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT32))
		goto get_next;
	group_id = (euint32)EN_GET_D(args, euint64);

	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT32))
		goto get_next;
	user_id = (euint32)EN_GET_D(args, euint64);

	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_UINT8))
		goto get_next;
	can_write = (bool)EN_GET_D(args, euint64);

	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_STRING))
		goto get_next;
	user_name = EN_GET_D(args, EventString *);

	if (!obj_array) {
		obj_array = object_array_new(50);
		object_array_set_object_finalize(obj_array, g_object_unref);
	}

	member_entry = member_entry_new(ES_GET(user_name), group_id, user_id,
					can_write);
	object_array_insert_object(obj_array, member_entry);

get_next:
	list = EN_GET_NEXT(list, EventList *);
	if (list)
		goto retry;
	return obj_array;
}

ObjectArray *
event_network_fetch_all_member_info(EventNetwork *self, euint32 group_id)
{
	ObjectArray *response_array = NULL;
	EventList   *list, *args;

	return_val_if_fail(self != NULL, event_network_fetch_all_member_info,
			   NULL);
	self->re_type = RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	args = event_list_new();
	event_list_add_data(args, EPTR((euint64)group_id), EVENT_TYPE_UINT32,
			    NULL);
	list = generate_send_list(self->online_id,
				  EXECUTE_TYPE_FETCH_ALL_MEMBER, args);

	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (eint32)EN_GET_D(args, eint64);
	if (self->re_type != RESPONSE_TYPE_SUCCESS)
		goto response_type_none_exit;
	args = EN_GET_NEXT(args, EventList *);
	if (!args || !event_list_verify_node_type(args, EVENT_TYPE_LIST))
		goto response_type_none_exit;
	args = EN_GET_D(args, EventList *);
	//
	response_array = parser_all_member_info(args);

	set_error_info(self, NULL);
response_type_none_exit:
	event_object_unref(list);
	return response_array;
}

ResponseType
event_network_modify_user_can_write(EventNetwork *self, euint32 group_id,
				    euint32 user_id, bool can_write)
{
	EventList *list, *args;

	return_val_if_fail(self != NULL, event_network_modify_can_join,
			   RESPONSE_TYPE_NONE);
	self->re_type = RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	args = event_list_new();
	event_list_add_data(args, EPTR((euint64)group_id), EVENT_TYPE_UINT32,
			    NULL);
	event_list_add_data(args, EPTR((euint64)user_id), EVENT_TYPE_UINT32,
			    NULL);
	event_list_add_data(args, EPTR((euint64)can_write), EVENT_TYPE_UINT8,
			    NULL);
	list = generate_send_list(self->online_id,
				  EXECUTE_TYPE_MODIFY_MEMBER_CAN_WRITE_FLAG,
				  args);

	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (euint32)EN_GET_D(args, euint64);

	set_error_info(self, NULL);
response_type_none_exit:
	event_object_unref(list);
	return self->re_type;
}

void
event_network_init_all(EventNetwork *self, const char *property_path)
{
	int status;
	return_if_fail(self != NULL, event_network_init_all);

	event_file_driver_set_file(EVENT_FILE_DRIVER(self), property_path);
	event_file_driver_set_file_not_exist_throw(EVENT_FILE_DRIVER(self),
						   false);
	status = event_file_driver_loader(EVENT_FILE_DRIVER(self));
	if (status != EVENT_FILE_DRIVER_SUCCESS) {
		event_debug(EventWindow, event_window_init,
			    "event_file_driver_loader status: %d", status);
	}
}

const char *
event_network_get_user_name(EventNetwork *self)
{
	return_val_if_fail(self != NULL, event_network_get_user_name, NULL);
	return ES_GET(self->user_name);
}

ResponseType
event_network_quit_group(EventNetwork *self, euint32 group_id)
{
	EventList *list, *args;

	return_val_if_fail(self != NULL, event_network_add_new_group, 0);
	self->re_type = RESPONSE_TYPE_NONE;

	if (network_prepare(self))
		goto response_type_none_exit;

	args = event_list_new();
	event_list_add_data(args, EPTR((euint64)group_id), EVENT_TYPE_UINT32,
			    NULL);
	list = generate_send_list(self->online_id,
				  EXECUTE_TYPE_USER_QUIT_FROM_GROUP, args);

	list = exchange_data_with_server(self, list);
	if (!list)
		goto response_type_none_exit;

	args	      = list;
	self->re_type = (euint32)EN_GET_D(args, euint64);

	set_error_info(self, NULL);
response_type_none_exit:
	event_object_unref(list);
	return self->re_type;
}
